/*-*-C-*-
 * Help request processing for Misc CSE
 */


#include "resed.h"
#include "main.h"

#include "swicall.h"
#include "wimp.h"
#include "resformat.h"
#include "newmsgs.h"

#include "menu.h"
#include "registry.h"

#include "format.h"
#include "relocate.h"
#include "objedit.h"

#include "object.h"
#include "help.h"



/*
 * On entry, s addresses a help reply string, and t addresses the first '!'
 *  inside this string.
 *
 * If t has the form !<id>[/param...]; then this is replaced by the value of
 *  message token !<id> with the given parameters substituted.
 *
 * If t does not have that form - or if the message token !<id> cannot be
 *  found - s is unchanged and the result is FALSE; otherwise the result is
 *  TRUE (even if the substitution had to be truncated).
 */

#define  MAX_PARAMS  3

static Bool help_expand (char *s, char *t)
{
    char *param[MAX_PARAMS];
    char *p = t;
    int i, j;
    char *text;
    char expansion[512];

    while (*p != '/' && *p != ';' && *p != 0) p++;
    if (*p == 0)
        return FALSE;

    for (i = 0; i < MAX_PARAMS; i++)
        param[i] = p;

    i = 0;
    while (*p != ';')
    {
        if (i == MAX_PARAMS)
            goto fail;

        *p = 0;
        p++;

        param[i] = p;
        i++;

        while (*p != '/' && *p != ';' && *p != 0) p++;
        if (*p == 0)
            goto fail;
    }
    *p = 0;

    text = message_lookup (&msgs, t);
    if (text == t)
    {
        *p = ';';
        goto fail;
    }
    sprintf (expansion, text, param[0], param[1], param[2]);

    p++;

    {
        char temp[MAX_HELP_TEXT];
        int len1 = strlen (p);
        int len2 = strlen (expansion);
        int left = MAX_HELP_TEXT - (t - s) - 1;

        strcpy (temp, p);

        if (len2 > left)
        {
            expansion[left] = 0;
            len2 = left;
        }
        left -= len2;
        strcpy (t, expansion);
        t += len2;

        if (len1 > left)
            temp[left] = 0;
        strcpy (t, temp);
    }

    return TRUE;

  fail:
    for (j = 0; j < i; j++)
        *(param[j] - 1) = '/';
    return FALSE;
}


/*
 * Look up the help text 'token' in the messages file.
 *
 * If the token is found, its text is copied to 'buf' and the result is TRUE.
 * If not, the result is FALSE.
 *
 * At most 'n' characters are copied to buf, which is guaranteed always to
 *  have a null terminator.
 */

Bool help_lookup_token (char *token, char *buf, int len)
{
    char *text = message_lookup (&msgs, token);

    if (text == token)    /* token not found in messages file */
        return FALSE;

    strncpy (buf, text, len-1);
    buf[len-1] = 0;

    return TRUE;
}


/*
 * Auxiliary function called from help_text_for_object_dbox(..) and from
 *  help_text_for_other_dbox(..) to check for the following tokens in the
 *  message file:
 *
 *    Hlp.<icon-name>
 *    Hlp.ADJ_UP         )
 *    Hlp.ADJ_DOWN       ) if <icon-name> ends in that string
 *    Hlp.MAX            )
 *
 * Returns TRUE iff one of them is found.
 */

static Bool check_for_icons (char *tag, char *token, char *reply)
{
    char *s;

    sprintf (token, "Hlp.%s", tag);
    if (help_lookup_token (token, reply, MAX_HELP_TEXT))
        return TRUE;

    s = strstr (tag, "ADJ_");
    if (s != NULL)
    {
        if (strcmp (s+4, "UP") == 0)
        {
            if (help_lookup_token ("Hlp.ADJ_UP", reply, MAX_HELP_TEXT))
                return TRUE;
        }
        if (strcmp (s+4, "DOWN") == 0)
        {
            if (help_lookup_token ("Hlp.ADJ_DOWN", reply, MAX_HELP_TEXT))
                return TRUE;
        }
    }

    s = strstr (tag, "MAX");
    if (s != NULL && s[3] == 0)
    {
        if (help_lookup_token ("Hlp.MAX", reply, MAX_HELP_TEXT))
            return TRUE;
    }

    return FALSE;
}


/*
 * Generate help text string for an icon in an object properties dialogue
 *  box.
 *
 * Tokens are looked up in the messages file in the following order; the
 *  first one found is returned as the help message:
 *
 *     Hlp.O.<object-class>.<icon-name>
 *     Hlp.<icon-name> etc.   [see check_for_icons(..) above]
 *     Hlp.O.<object-class>
 *     Hlp.O
 *
 *  where <object-class> is the name of the object's dbox' template, and
 *   <icon-name> is the !WinEdit name of the icon.
 *
 * If no text is found, the result is FALSE.
 */

static Bool help_text_for_object_dbox (
    ObjectDefPtr def,
    int w,
    int icon,
    char *reply
)
{
    char tag[50], token[50];

    wimp_read_icon_name (w, icon, tag);

    if (*tag != 0)   /* icon has a name */
    {
        sprintf (token, "Hlp.O.%s.%s", def->templatename, tag);
        if (help_lookup_token (token, reply, MAX_HELP_TEXT))
            return TRUE;

        if (check_for_icons (tag, token, reply))
            return TRUE;
    }

    sprintf (token, "Hlp.O.%s", def->templatename);
    if (help_lookup_token (token, reply, MAX_HELP_TEXT))
        return TRUE;

    return help_lookup_token ("Hlp.O", reply, MAX_HELP_TEXT);
}


/*
 * Generate help text string for an icon in a dialogue box (other than an
 *  ObjectDbox).
 *
 * Tokens are looked up in the messages file in the following order; the
 *  first one found is returned as the help message:
 *
 *     Hlp.W<n>.<icon-name>
 *     Hlp.<icon-name> etc.   [see check_for_icons(..) above]
 *     Hlp.W<n>
 *
 *  where <n> is the dbox's registered type, and <icon-name> is the !WinEdit
 *  name of the icon.
 *
 * If no text is found, the result is FALSE.
 */

static Bool help_text_for_other_dbox (
    RegistryType type,
    int w,
    int icon,
    char *reply
)
{
    char tag[50], token[50];

    wimp_read_icon_name (w, icon, tag);

    if (*tag != 0)   /* icon has a name */
    {
        sprintf (token, "Hlp.W%d.%s", type, tag);
        if (help_lookup_token (token, reply, MAX_HELP_TEXT))
            return TRUE;

        if (check_for_icons (tag, token, reply))
            return TRUE;
    }

    sprintf (token, "Hlp.W%d", type);
    return help_lookup_token (token, reply, MAX_HELP_TEXT);
}


/*
 * Generate help text for the current menu entry.
 *
 * This is done by looking up tokens in the following order; the first found
 *  is returned as the help text:
 *
 *    Hlp.M<n>.h1.h2.h3
 *    Hlp.M<n>.h1.h2
 *    Hlp.M<n>.h1
 *    Hlp.M<n>
 *
 *  where <n> is the registered MenuType, and h1.h2.h3... is the "hit list".
 *
 * Result is TRUE iff a string matching this token is found in the messages
 *  file.
 */

static Bool help_text_for_menu_entry (char *reply)
{
    int buf[10];
    int *p = buf;
    char *s = reply;
    char *text;
    int type;

    /* determine state of current menu (if any) */
    if (swi (Wimp_GetMenuState, R0, 0, R1, buf, END) != NULL || *buf == -1)
        return FALSE;

    /* lookup type of current menu */
    type = menu_lookup_type ();
    if (type == UNKNOWN_MENU)
        return FALSE;

    /* construct tag */
    sprintf (reply, "Hlp.M%d", type);
    s = reply + 3;   /* points to first separator */
    while (*p != -1)
    {
        s += strlen(s);
        sprintf(s, ".%d", *p);
        p++;
    }

    /* lookup message text */
    while (TRUE)
    {
        text = message_lookup (&msgs, reply);

        if (text != reply)
            break;

        /* remove final component and try again */
        *s = 0;
        while (s != reply && *s != '.')
            s--;

        if (s == reply)
            return FALSE;
    }

    strcpy (reply, text);
    return TRUE;
}


/*
 * Turn a help message around.
 */

error * help_message (MessageHelpRequestPtr req)
{
    MessageHelpReplyPtr reply = (MessageHelpReplyPtr) req;
    RegistryType type;
    int w = req->mouse.windowhandle;
    int icon = req->mouse.iconhandle;

    if (w == -1)  /* it's not in a window */
        return NULL;

    else          /* it's in a window */
    {
        void *closure;

        /* find out which kind of window */
        type = registry_lookup_window (w, &closure);

        switch (type)
        {
        case Unknown:    /* probably in a menu */
            if (!help_text_for_menu_entry (reply->help))
                return NULL;
            break;

        case ObjectDbox: /* generic dialogue box */
            if (!help_text_for_object_dbox (((ObjectPtr)closure)->def,
                                                     w, icon, reply->help))
                return NULL;
            break;

        default:         /* other dialogue boxes */
            if (!help_text_for_other_dbox (type, w, icon, reply->help))
                return NULL;
            break;
        }
    }

    /* expand any internal references to other tokens */
    {
        char *t = reply->help;

        while (TRUE)
        {
            t = strchr (t, '!');
            if (t == NULL)
                break;
            if (!help_expand (reply->help, t))
                t++;
        }
    }

    /* construct and send reply */
    reply->header.yourref = req->header.myref;
    reply->header.messageid = MESSAGE_HELP_REPLY;
    reply->header.size = (sizeof(MessageHeaderRec) +
                               strlen(reply->help) + 4) & ~3;

    return swi (Wimp_SendMessage,  NONX,
                R0, EV_USER_MESSAGE,
                R1, reply,
                R2, req->header.taskhandle,  END);
}
